﻿using Loong.Admin.Security.Cryptography;
using Loong.Admin.Users;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;

namespace Loong.Admin.Account
{
    public class AccountManager : DomainService
    {
        private readonly SignInOptions _signInOptions;
        private readonly LockoutOptions _lockoutOptions;
        private readonly IPasswordHasher _passwordHasher;
        private readonly UserManager _userManager;

        public AccountManager(
            IOptions<SignInOptions> signInOptionsAccessor,
            IOptions<LockoutOptions> lockoutOptionsAccessor,
            IPasswordHasher passwordHasher,
            UserManager userManager)
        {
            _signInOptions= signInOptionsAccessor.Value;
            _lockoutOptions= lockoutOptionsAccessor.Value;
            _passwordHasher= passwordHasher;
            _userManager=userManager;
        }

        public async Task<SignInResult> SignInAsync(User? user, string password)
        {
            if (user == null)
            {
                return SignInResult.InvalidUserNameOrPassword;
            }

            var result = PreSignInCheck(user);
            if (result != SignInResult.Success) return result;

            if (await CheckPasswordAsync(user, password))
            {
                if (user.ShouldChangePasswordOnNextLogin)
                {
                    return SignInResult.ShouldChangePassword;
                }

                return SignInResult.Success;
            }

            AccessFailed(user);
            if (user.IsLockedOut(Clock.Now))
            {
                return SignInResult.LockedOut;
            }

            return SignInResult.InvalidUserNameOrPassword;
        }

        private SignInResult PreSignInCheck(User user)
        {
            if (_signInOptions.RequireConfirmedEmail && !user.EmailConfirmed)
            {
                return SignInResult.NotAllowed;
            }

            if (_signInOptions.RequireConfirmedPhoneNumber && !user.PhoneNumberConfirmed)
            {
                return SignInResult.NotAllowed;
            }

            if (!user.IsActive)
            {
                return SignInResult.NotAllowed;
            }

            if (user.IsLockedOut(Clock.Now))
            {
                return SignInResult.LockedOut;
            }

            return SignInResult.Success;
        }

        public async Task<bool> CheckPasswordAsync(User user, string password)
        {
            var result = _passwordHasher.VerifyHashedPassword(user.PasswordHash!, password);
            if (result == PasswordVerificationResult.SuccessRehashNeeded)
            {
                await _userManager.UpdatePasswordHashAsync(user, password, false);
            }

            return result != PasswordVerificationResult.Failed;
        }

        private void AccessFailed(User user)
        {
            user.IncrementAccessFailedCount();
            if (user.AccessFailedCount < _lockoutOptions.MaxFailedAccessAttempts) return;

            user.SetLockoutEnd(Clock.Now.Add(_lockoutOptions.DefaultLockoutTimeSpan));
            user.ResetAccessFailedCount();
        }
    }
}
